﻿//-----------------------------------------------------------------------
// <copyright file="SessionManagerTest.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings

using System;
using System.Collections.Generic;
using GateShift.Servers.GameServer.Managers.Connection;
using GateShift.Servers.GameServer.Managers.Session;
using GateShift.Services.SDK.Services;
using GateShift.Services.SDK.Services.HostIntegration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

#endregion

namespace GateShift.Testing.Servers.GameServer.Managers
{
    /// <summary>
    /// Unit Test Class for SessionManagerTest
    /// </summary>
    [TestClass]
    public class SessionManagerTest
    {
        private SessionManager _manager;
        private SessionManager_Accessor _accessor;
        private MockFactory _mocks;

        /// <summary>
        /// Gets or sets the test context which provides
        /// information about and functionality for the current test run.
        /// </summary>
        public TestContext TestContext { get; set; }

        #region Additional test attributes

        /// <summary>
        /// Use TestInitialize to run code before running each test
        /// </summary>
        [TestInitialize]
        public void MyTestInitialize()
        {
            _manager = new SessionManager();
            _accessor = SessionManager_Accessor.AttachShadow(_manager);
            _mocks = new MockFactory(MockBehavior.Strict);
        }

        /// <summary>
        /// Use TestCleanup to run code after each test has run
        /// </summary>
        [TestCleanup]
        public void MyTestCleanup()
        {
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Test for NormalConstructor on Initializing Variables
        /// </summary>
        [TestMethod]
        public void NormalConstructor_InitializesVariables()
        {
            Assert.IsNotNull(_accessor._clientSessions);
            Assert.IsNotNull(_accessor._connectionReferences);
            Assert.AreEqual(0, _accessor._connectionReferences.Count);
            Assert.IsNotNull(_accessor._packetHandlers);
            Assert.AreEqual(0, _accessor._packetHandlers.Count);
            Assert.IsNotNull(_accessor._sessionReferences);
            Assert.AreEqual(0, _accessor._sessionReferences.Count);
        }

        #endregion

        #region BackGround Threading

        /// <summary>
        /// Test for BackGroundThread
        /// </summary>
        [TestMethod]
        public void BackGroundThread()
        {
            Assert.AreEqual(false, _manager.IsAlive);
            _manager.Start();
            Assert.AreEqual(true, _manager.IsAlive);
            _manager.Stop();
            Assert.AreEqual(false, _manager.IsAlive);
        }

        #endregion

        #region Event Raising

        /// <summary>
        /// Test for SessionConnected on calling the event
        /// </summary>
        [TestMethod]
        public void SessionConnected_CallsEvent()
        {
            // We can use a Lambda to create a method on the fly that will set our local variable
            // Signifying the event was called. This will prevent excess code.
            bool eventCalled = false;
            _manager.SessionConnected += (sender, e) => eventCalled = true;

            _accessor.OnSessionConnected(null);

            Assert.AreEqual(true, eventCalled);
        }

        /// <summary>
        /// Test for SessionDisconnected on calling the event
        /// </summary>
        [TestMethod]
        public void SessionDisconnected_CallsEvent()
        {
            // We can use a Lambda to create a method on the fly that will set our local variable
            // Signifying the event was called. This will prevent excess code.
            bool eventCalled = false;
            _manager.SessionDisconnected += (sender, e) => eventCalled = true;

            _accessor.OnSessionDisconnected(null);

            Assert.AreEqual(true, eventCalled);
        }

        #endregion

        #region ConnectionManager Events

        /// <summary>
        /// Test for ConnectionManager on calling the event for ClientPacketRecieved without needing authentication
        /// </summary>
        [TestMethod]
        public void ConnectionManager_ClientPacketRecieved_NoAuthNeeded()
        {
            ClientPacket packet = new ClientPacket { PacketData = null, ServiceCode = 0, ServiceID = 1 };
            ClientPacketEventArgs args = new ClientPacketEventArgs(Guid.NewGuid(), packet);

            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);
            _accessor._sessionReferences.Add(session.SessionID, args.ClientID);
            _accessor._connectionReferences.Add(args.ClientID, session.SessionID);

            IMock<IClientDataHandler> mock = _mocks.Create<IClientDataHandler>();

            mock.Expect(t => t.HandleClientData(session, 0, null, false)).Verifiable();

            _accessor._packetHandlers.Add(1, mock.Object);

            _accessor.ConnectionManager_ClientPacketRecieved(null, args);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for ConnectionManager on calling the event for ClientPacketRecieved needing authentication but not logged in
        /// </summary>
        [TestMethod]
        public void ConnectionManager_ClientPacketRecieved_AuthNeeded_NotLoggedIn()
        {
            ClientPacket packet = new ClientPacket { PacketData = null, ServiceCode = 0, ServiceID = 2 };
            ClientPacketEventArgs args = new ClientPacketEventArgs(Guid.NewGuid(), packet);

            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);
            _accessor._sessionReferences.Add(session.SessionID, args.ClientID);
            _accessor._connectionReferences.Add(args.ClientID, session.SessionID);

            IMock<IAuthenticationClientDataHandler> mock = _mocks.Create<IAuthenticationClientDataHandler>();

            mock.Expect(t => t.HandleUnauthenticatedData(It.IsAny<IClientSession>())).Verifiable();

            _accessor._packetHandlers.Add(1, mock.Object);
            _accessor._authenticationServiceID = 1;

            _accessor.ConnectionManager_ClientPacketRecieved(null, args);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for ConnectionManager on calling the event for ClientPacketRecieved needing authentication, not logged in, and on authentication service id
        /// </summary>
        [TestMethod]
        public void ConnectionManager_ClientPacketRecieved_AuthNeeded_SameServiceID()
        {
            ClientPacket packet = new ClientPacket { PacketData = null, ServiceCode = 0, ServiceID = 1 };
            ClientPacketEventArgs args = new ClientPacketEventArgs(Guid.NewGuid(), packet);

            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);
            _accessor._sessionReferences.Add(session.SessionID, args.ClientID);
            _accessor._connectionReferences.Add(args.ClientID, session.SessionID);

            IMock<IAuthenticationClientDataHandler> mock = _mocks.Create<IAuthenticationClientDataHandler>();

            mock.Expect(t => t.HandleClientData(session, 0, null, false)).Verifiable();

            _accessor._packetHandlers.Add(1, mock.Object);
            _accessor._authenticationServiceID = 1;

            _accessor.ConnectionManager_ClientPacketRecieved(null, args);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for ConnectionManager on calling the event for ClientPacketRecieved needing authentication and being logged in
        /// </summary>
        [TestMethod]
        public void ConnectionManager_ClientPacketRecieved_AuthNeeded_LoggedIn()
        {
            ClientPacket packet = new ClientPacket { PacketData = null, ServiceCode = 0, ServiceID = 1 };
            ClientPacketEventArgs args = new ClientPacketEventArgs(Guid.NewGuid(), packet);

            ClientSession session = new ClientSession(_manager);
            session.RenewSessionToken();
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);
            _accessor._sessionReferences.Add(session.SessionID, args.ClientID);
            _accessor._connectionReferences.Add(args.ClientID, session.SessionID);

            IMock<IAuthenticationClientDataHandler> mock = _mocks.Create<IAuthenticationClientDataHandler>();

            mock.Expect(t => t.HandleClientData(session, 0, null, false)).Verifiable();

            _accessor._packetHandlers.Add(1, mock.Object);
            _accessor._authenticationServiceID = 1;

            _accessor.ConnectionManager_ClientPacketRecieved(null, args);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for ConnectionManager on calling the event for ClientDisconnected
        /// </summary>
        [TestMethod]
        public void ConnectionManager_ClientDisconnected()
        {
            Guid connectionID = Guid.NewGuid();
            ClientSession session = new ClientSession(_manager);
            ClientEventArgs args = new ClientEventArgs(connectionID);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);
            _accessor._sessionReferences.Add(session.SessionID, connectionID);
            _accessor._connectionReferences.Add(connectionID, session.SessionID);

            _accessor.ConnectionManager_ClientDisconnected(null, args);

            Assert.IsFalse(_accessor._connectionReferences.ContainsKey(connectionID));
            Assert.IsFalse(_accessor._sessionReferences.ContainsKey(session.SessionID));
            Assert.IsFalse(((ClientSessionCollection)_accessor._clientSessions.Target).ContainsKey(session.SessionID));
        }

        /// <summary>
        /// Test for ConnectionManager on calling the event for ClientConnected
        /// </summary>
        [TestMethod]
        public void ConnectionManager_ClientConnected()
        {
            Guid connectionID = Guid.NewGuid();
            ClientEventArgs args = new ClientEventArgs(connectionID);

            _accessor.ConnectionManager_ClientConnected(null, args);

            Assert.IsTrue(_accessor._connectionReferences.ContainsKey(connectionID));

            Guid sessionID = _accessor._connectionReferences[connectionID];

            Assert.IsTrue(_accessor._sessionReferences.ContainsKey(sessionID));
            Assert.IsTrue(((ClientSessionCollection)_accessor._clientSessions.Target).ContainsKey(sessionID));
        }

        #endregion

        #region DisconnectClient

        /// <summary>
        /// Test for Disconnecting the Client
        /// </summary>
        [TestMethod]
        public void DisconnectClient()
        {
            Guid connectionID = Guid.NewGuid();
            Guid sessionID = Guid.NewGuid();

            IMock<IConnectionManager> mock = _mocks.Create<IConnectionManager>();
            mock.Expect(t => t.Disconnect(connectionID)).Verifiable();
            _manager.ConnectionManager = mock.Object;
            _accessor._sessionReferences.Add(sessionID, connectionID);
            _accessor.DisconnectClient(sessionID);
            mock.VerifyAll();
        }

        #endregion

        #region GetSession

        /// <summary>
        /// Test for getting a session; session is found
        /// </summary>
        [TestMethod]
        public void GetSession_SessionFound()
        {
            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);

            Assert.AreEqual(session, _manager.GetSession(session.SessionID));
        }

        /// <summary>
        /// Test for getting a session; session is not found
        /// </summary>
        [TestMethod]
        public void GetSession_SessionNotFound()
        {
            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);

            Assert.IsNull(_manager.GetSession(Guid.NewGuid()));
        }

        /// <summary>
        /// Test for getting a list of sessions; sessions are not found
        /// </summary>
        [TestMethod]
        public void GetSession_EmptyItemList()
        {
            IList<IClientSession> items = _manager.GetSession();

            Assert.IsNotNull(items);
            Assert.AreEqual(0, items.Count);
        }

        /// <summary>
        /// Test for getting a list of sessions; 2 sessions are found
        /// </summary>
        [TestMethod]
        public void GetSession_2ItemList()
        {
            ClientSession session1 = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session1.SessionID, session1);

            ClientSession session2 = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session2.SessionID, session2);

            IList<IClientSession> items = _manager.GetSession();

            Assert.AreEqual(2, items.Count);
            Assert.AreEqual(session1, items[0]);
            Assert.AreEqual(session2, items[1]);
        }

        #endregion

        #region SessionExists

        /// <summary>
        /// Test for checking for a session; session is found
        /// </summary>
        [TestMethod]
        public void SessionExists_SessionFound()
        {
            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);

            Assert.IsTrue(_manager.SessionExists(session.SessionID));
        }

        /// <summary>
        /// Test for checking for a session; session is not found
        /// </summary>
        [TestMethod]
        public void SessionExists_SessionNotFound()
        {
            ClientSession session = new ClientSession(_manager);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);

            Assert.IsFalse(_manager.SessionExists(Guid.NewGuid()));
        }

        #endregion

        #region SendClientData

        /// <summary>
        /// Test for sending client data; session is not found
        /// </summary>
        [TestMethod]
        public void SendClientData_MissingSession()
        {
            IMock<IConnectionManager> mock = _mocks.Create<IConnectionManager>();
            _manager.ConnectionManager = mock.Object;

            _manager.SendClientData(Guid.NewGuid(), 1, 2, null, false);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for sending client data; 1 session
        /// </summary>
        [TestMethod]
        public void SendClientData_SingleSession()
        {
            ClientSession session = new ClientSession(_manager);
            Guid clientID = Guid.NewGuid();
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session.SessionID, session);
            _accessor._sessionReferences.Add(session.SessionID, clientID);

            IMock<IConnectionManager> mock = _mocks.Create<IConnectionManager>();
            _manager.ConnectionManager = mock.Object;

            mock.Expect(t => t.SendData(clientID, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();

            _manager.SendClientData(session.SessionID, 1, 2, null, false);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for sending client data; 2 sessions
        /// </summary>
        [TestMethod]
        public void SendClientData_MultipleSession()
        {
            ClientSession session1 = new ClientSession(_manager);
            ClientSession session2 = new ClientSession(_manager);
            ClientSession session3 = new ClientSession(_manager);

            Guid clientID1 = Guid.NewGuid();
            Guid clientID2 = Guid.NewGuid();
            Guid clientID3 = Guid.NewGuid();

            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session1.SessionID, session1);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session2.SessionID, session2);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session3.SessionID, session3);

            _accessor._sessionReferences.Add(session1.SessionID, clientID1);
            _accessor._sessionReferences.Add(session2.SessionID, clientID2);
            _accessor._sessionReferences.Add(session3.SessionID, clientID3);

            IMock<IConnectionManager> mock = _mocks.Create<IConnectionManager>();
            _manager.ConnectionManager = mock.Object;

            mock.Expect(t => t.SendData(clientID1, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();
            mock.Expect(t => t.SendData(clientID2, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();

            _manager.SendClientData(new List<Guid> { session1.SessionID, session2.SessionID }, 1, 2, null, false);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for sending client data; 3 sessions, 2 found
        /// </summary>
        [TestMethod]
        public void SendClientData_MultipleSessionOneMissing()
        {
            ClientSession session1 = new ClientSession(_manager);
            ClientSession session2 = new ClientSession(_manager);
            ClientSession session3 = new ClientSession(_manager);

            Guid clientID1 = Guid.NewGuid();
            Guid clientID2 = Guid.NewGuid();
            Guid clientID3 = Guid.NewGuid();

            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session1.SessionID, session1);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session2.SessionID, session2);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session3.SessionID, session3);

            _accessor._sessionReferences.Add(session1.SessionID, clientID1);
            _accessor._sessionReferences.Add(session2.SessionID, clientID2);
            _accessor._sessionReferences.Add(session3.SessionID, clientID3);

            IMock<IConnectionManager> mock = _mocks.Create<IConnectionManager>();
            _manager.ConnectionManager = mock.Object;

            mock.Expect(t => t.SendData(clientID1, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();
            mock.Expect(t => t.SendData(clientID2, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();

            _manager.SendClientData(new List<Guid> { session1.SessionID, session2.SessionID, Guid.NewGuid() }, 1, 2, null, false);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for sending client data; 3 sessions
        /// </summary>
        [TestMethod]
        public void SendClientData_AllSession()
        {
            ClientSession session1 = new ClientSession(_manager);
            ClientSession session2 = new ClientSession(_manager);
            ClientSession session3 = new ClientSession(_manager);

            Guid clientID1 = Guid.NewGuid();
            Guid clientID2 = Guid.NewGuid();
            Guid clientID3 = Guid.NewGuid();

            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session1.SessionID, session1);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session2.SessionID, session2);
            ((ClientSessionCollection)_accessor._clientSessions.Target).Add(session3.SessionID, session3);

            _accessor._sessionReferences.Add(session1.SessionID, clientID1);
            _accessor._sessionReferences.Add(session2.SessionID, clientID2);
            _accessor._sessionReferences.Add(session3.SessionID, clientID3);

            IMock<IConnectionManager> mock = _mocks.Create<IConnectionManager>();
            _manager.ConnectionManager = mock.Object;

            mock.Expect(t => t.SendData(clientID1, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();
            mock.Expect(t => t.SendData(clientID2, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();
            mock.Expect(t => t.SendData(clientID3, It.Is<ClientPacket>(i => i.ServiceCode == 2 && i.ServiceID == 1 && i.PacketData == null))).Verifiable();

            _manager.SendClientData(1, 2, null, false);

            mock.VerifyAll();
        }

        #endregion

        #region RegisterClientDataHandler

        /// <summary>
        /// Test for RegisterClientDataHandler successfully
        /// </summary>
        [TestMethod]
        public void RegisterClientDataHandler_Success()
        {
            IMock<IClientDataHandler> mock = _mocks.Create<IClientDataHandler>();

            _manager.RegisterClientDataHandler(1, mock.Object);

            Assert.AreEqual(1, _accessor._packetHandlers.Count);
            Assert.AreEqual(mock.Object, _accessor._packetHandlers[1]);

            mock.VerifyAll();
        }

        /// <summary>
        /// Test for RegisterClientDataHandler; service id already exists
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void RegisterClientDataHandler_ServiceIDExists()
        {
            IMock<IClientDataHandler> mock = _mocks.Create<IClientDataHandler>();

            _accessor._packetHandlers.Add(1, mock.Object);

            _manager.RegisterClientDataHandler(1, mock.Object);

            mock.VerifyAll();
        }

        #endregion
    }
}